package handler

import (
	"gitee.com/simple-set/simple.proxy/src/utils"
	"io"
	"log/slog"
	"strings"
)

type rule struct {
	regexp       string
	matchPattern string // 匹配模式: prefix前缀匹配、suffix后缀匹配、match完全匹配
	action       string // allow 允许, deny 拒绝
}

func newRule(regexp string) *rule {
	if split := strings.Split(regexp, " "); len(split) == 2 {
		if !(split[0] == "allow" || split[0] == "deny") {
			slog.Warn("access control rules are illegal: " + regexp)
			return nil
		}
		r := &rule{action: strings.TrimSpace(split[0])}
		if strings.HasPrefix(split[1], "*") {
			r.matchPattern = "suffix"
			if i := strings.Split(strings.TrimSpace(split[1]), "*"); len(i) == 2 {
				r.regexp = i[1]
			} else {
				slog.Warn("access control rules are illegal: " + regexp)
				return nil
			}
		} else if strings.HasSuffix(split[1], "*") {
			r.matchPattern = "prefix"
			if i := strings.Split(strings.TrimSpace(split[1]), "*"); len(i) == 2 {
				r.regexp = i[0]
			} else {
				slog.Warn("access control rules are illegal: " + regexp)
				return nil
			}
		} else {
			r.matchPattern = "match"
			r.regexp = split[1]
		}
		return r
	}
	return nil
}

// Interceptor ACL访问控制列表, 根据规则放行或拒绝请求
type Interceptor struct {
	ruleFile    string
	rules       []*rule
	matchResult map[string]bool // TODO 限制缓存长度, 最好是用LRU、LFU缓存模式
}

// NewInterceptor 构造函数, 从指定文件加载控制规则
func NewInterceptor(ruleFile string) *Interceptor {
	fileCache, err := utils.NewFileCache(ruleFile)
	if err != nil {
		slog.Error("Loading ruleFile exception " + err.Error())
		return &Interceptor{ruleFile: ruleFile}
	}
	defer fileCache.Close()

	rules := make([]*rule, 0)
	for {
		line, err := fileCache.ReadLine()
		if err == io.EOF {
			break
		}
		if err != nil {
			slog.Error("Loading ruleFile exception " + err.Error())
			break
		}
		if strings.HasPrefix(strings.TrimSpace(line), "#") {
			continue
		}
		if strings.TrimSpace(line) == "" {
			continue
		}
		if split := strings.Split(strings.TrimSpace(line), " "); len(split) == 2 {
			if r := newRule(line); r != nil {
				rules = append(rules, r)
			}
		}
	}
	return &Interceptor{ruleFile: ruleFile, rules: rules, matchResult: make(map[string]bool, 20)}
}

// 匹配拦截规则
func (i *Interceptor) ruleMatch(rule *rule, challenge string) (match, result bool) {
	if rule.matchPattern == "all" {
		return true, rule.action == "deny"
	}
	if rule.matchPattern == "prefix" && strings.HasPrefix(challenge, rule.regexp) {
		match = true
	} else if rule.matchPattern == "suffix" && strings.HasSuffix(challenge, rule.regexp) {
		match = true
	} else if rule.matchPattern == "match" && challenge == rule.regexp {
		match = true
	}
	if match {
		return match, rule.action == "deny"
	}
	return false, false
}

// Intercept 访问过滤器, true:拦截访问, false:允许访问
func (i *Interceptor) Intercept(challenge string) bool {
	if i.rules == nil {
		return false
	}
	if result, exist := i.matchResult[challenge]; exist {
		return result
	}
	for _, rule := range i.rules {
		if match, result := i.ruleMatch(rule, challenge); match {
			i.matchResult[challenge] = result
			return result
		}
	}
	return false
}
